package com.googlecode.lightest.core

import org.codehaus.groovy.runtime.InvokerHelper
import org.testng.annotations.*
import org.testng.ITestContext

class LightestTestCase extends GroovyTestCase {
    IDispatcherAssignmentStrategy strategy
    IDomainSpecificApi api
    ITaskDispatcher dispatcher
    
    def builder
    
    LightestTestCase() {
        builder = new TaskNodeBuilder()
    }

    @BeforeMethod
    void assign(ITestContext context) {
        strategy = context.getAttribute(LightestTestListener.ATTR_STRATEGY)
        strategy.assign(this)
    }
    
    @AfterMethod
    void unassign() {
        strategy.unassign(this)
    }
    
    /**
     * In order to enable any domain specific API's in this testcase, the API
     * should be set with this method. One technique is to subclass this class,
     * set the API in a @BeforeTest method, and derive test classes from that
     * subclass.
     * 
     * @param api
     */
    void setApi(IDomainSpecificApi api) {
        this.api = api 
    }
    
    /**
     * The ITaskDispatcher is injected into the testcase.
     *
     * @param dispatcher
     */
    void setDispatcher(ITaskDispatcher dispatcher) {
        this.dispatcher = dispatcher
    }
    
    IDomainSpecificApi getApi() {
        return api
    }
    /**
     * Returns the currently assigned task dispatcher, or null..
     */
    ITaskDispatcher getDispatcher() {
        return dispatcher
    }
    
    /**
     * Attempts to treat calls to missing methods as API task invocations.
     * 
     * Task invocations may be specified with a builder-like nested syntax. If
     * specified in this way, they will be invoked in depth-first order, with
     * the results of the parent tasks available to be referenced by the child
     * tasks via the task result hierarchy. The configuration of parent tasks
     * is also available to child tasks.
     * 
     * If a parent task fails, the child tasks will not be performed. However,
     * sibling tasks will be performed.
     */
    def methodMissing(String name, args) {
        setBuilderAsDelegate(args)
        
        def taskTree = builder.invokeMethod(name, args)
        
        dispatcher.dispatch(taskTree)
    }
    
    /**
     * Sets the internal builder instance as the delegate for any Closures in
     * the argument list.
     *
     * @param args  a list of arguments as would be passed into methodMissing()
     *              or invokeMethod().
     */
    private setBuilderAsDelegate(args) {
        def argsList = InvokerHelper.asList(args)
        
        argsList.each {
            if (it instanceof Closure) {
                it.setDelegate(builder)
                it.setResolveStrategy(Closure.DELEGATE_FIRST)
            }
        }
    }
}
